const Utils = require("../utils");
const tencentcloud = require("tencentcloud-sdk-nodejs");
const moment = require("moment");
const Redis = require("../utils/Redis");
const smsClient = tencentcloud.sms.v20210111.Client;

const tencentSMS = Utils.getConfig().tencentSMS;

/* 实例化腾讯云的client对象 */
const tencentClient = new smsClient({
  credential: {
    secretId: tencentSMS.secretId,
    secretKey: tencentSMS.secretKey,
  },
  /* 必填：地域信息，可以直接填写字符串ap-guangzhou，支持的地域列表参考 https://cloud.tencent.com/document/api/382/52071#.E5.9C.B0.E5.9F.9F.E5.88.97.E8.A1.A8 */
  region: "ap-guangzhou",
  /* 非必填:
   * 客户端配置对象，可以指定超时时间等配置 */
  profile: {
    /* SDK默认用TC3-HMAC-SHA256进行签名，非必要请不要修改这个字段 */
    signMethod: "HmacSHA256",
    httpProfile: {
      /* SDK默认使用POST方法。
       * 如果您一定要使用GET方法，可以在这里设置。GET方法无法处理一些较大的请求 */
      reqMethod: "POST",
      /* SDK有默认的超时时间，非必要请不要进行调整
       * 如有需要请在代码中查阅以获取最新的默认值 */
      reqTimeout: 30,
      /**
       * 指定接入地域域名，默认就近地域接入域名为 sms.tencentcloudapi.com ，也支持指定地域域名访问，例如广州地域的域名为 sms.ap-guangzhou.tencentcloudapi.com
       */
      endpoint: "sms.tencentcloudapi.com",
    },
  },
});

/** 腾讯云请求参数 */
function tencentParams() {
  return {
    /* 短信应用ID: 短信SmsSdkAppId在 [短信控制台] 添加应用后生成的实际SmsSdkAppId，示例如1400006666 */
    // 应用 ID 可前往 [短信控制台](https://console.cloud.tencent.com/smsv2/app-manage) 查看
    SmsSdkAppId: tencentSMS.appid,
    /* 短信签名内容: 使用 UTF-8 编码，必须填写已审核通过的签名 */
    // 签名信息可前往 [国内短信](https://console.cloud.tencent.com/smsv2/csms-sign) 或 [国际/港澳台短信](https://console.cloud.tencent.com/smsv2/isms-sign) 的签名管理查看
    SignName: tencentSMS.smsSign,
    /* 模板 ID: 必须填写已审核通过的模板 ID */
    // 模板 ID 可前往 [国内短信](https://console.cloud.tencent.com/smsv2/csms-template) 或 [国际/港澳台短信](https://console.cloud.tencent.com/smsv2/isms-template) 的正文模板管理查看
    TemplateId: tencentSMS.templateId,
    /* 模板参数: 模板参数的个数需要与 TemplateId 对应模板的变量个数保持一致，若无模板参数，则设置为空 */
    TemplateParamSet: [],
    /* 下发手机号码，采用 e.164 标准，+[国家或地区码][手机号]
     * 示例如：+8613711112222， 其中前面有一个+号 ，86为国家码，13711112222为手机号，最多不要超过200个手机号*/
    PhoneNumberSet: [],
    /* 用户的 session 内容（无需要可忽略）: 可以携带用户侧 ID 等上下文信息，server 会原样返回 */
    SessionContext: "",
    /* 短信码号扩展号（无需要可忽略）: 默认未开通，如需开通请联系 [腾讯云短信小助手] */
    ExtendCode: "",
    /* 国内短信无需填写该项；国际/港澳台短信已申请独立 SenderId 需要填写该字段，默认使用公共 SenderId，无需填写该字段。注：月度使用量达到指定量级可申请独立 SenderId 使用，详情请联系 [腾讯云短信小助手](https://cloud.tencent.com/document/product/382/3773#.E6.8A.80.E6.9C.AF.E4.BA.A4.E6.B5.81)。 */
    SenderId: "",
  };
}
/**
 * 发送短信验证码
 */
class SMS {
  /**
   * 腾讯云发送短信验证码
   * @param {string} phoneNumber 手机号
   * @param {string} client_ip 客户端IP
   */
  static sendTencentSMS(phoneNumber, client_ip) {
    return new Promise(async (resolve, reject) => {
      if (!SMS.isPhone(phoneNumber)) {
        reject({ code: 400, message: "手机号码格式不正确" });
        return;
      }
      if (!client_ip) {
        reject({ code: 400, message: "无法获取客户端IP地址" });
        return;
      }
      try {
        // 获取今天的时间
        const today = moment(new Date()).format("YYYY-MM-DD");
        // rediskey
        const redisIpKey = `sms-ip-${client_ip}-${today}`;
        const redisPhoneKey = `sms-phone-${phoneNumber}`;
        // 获取redis中的IP今天发送验证码次数
        const ipNumber = await Redis.get(redisIpKey);
        // 如果超过30次，则不发送
        if (Number(ipNumber) >= tencentSMS.ipMaxCount) {
          reject({
            code: 400,
            message: "您今天发送验证码次数已达上限，请明天再试",
          });
          return;
        }
        // 获取redis中的手机号发送记录
        const phoneTime = await Redis.get(redisPhoneKey);
        if (phoneTime) {
          reject({ code: 400, message: "请勿频繁发送短信验证码" });
          return;
        }
        // 获取腾讯云请求参数
        const params = tencentParams();
        params.PhoneNumberSet = [`+86${phoneNumber}`];
        const uuid = Utils.createUUID();
        const code = Math.floor(Math.random() * 10000);
        params.TemplateParamSet = [code, (tencentSMS.indate / 60).toString()];
        const result = await tencentClient.SendSms(params);
        if (result.SendStatusSet[0]?.Code !== "Ok") {
          reject(result.SendStatusSet[0]?.Message);
          return;
        }
        // 将验证码存入redis
        await Redis.set(`sms-code-${phoneNumber}`, JSON.stringify({ code, uuid }), "EX", tencentSMS.indate);
        // 记录IP发送次数
        await Redis.set(redisIpKey, ipNumber ? Number(ipNumber) + 1 : 1, "EX", 86400);

        // 记录手机号发送时间
        await Redis.set(redisPhoneKey, 1, "EX", tencentSMS.interval);
        resolve(uuid);
      } catch (error) {
        reject(error);
      }
    });
  }

  /**
   * 校验短信验证码
   * @param {string} phoneNumber 手机号
   * @param {string} uuid uuid
   * @param {string} code 验证码
   * @returns {Promise<boolean>}
   */
  static checkSMS(phoneNumber, uuid, code) {
    return new Promise(async (resolve, reject) => {
      // 校验手机号码
      if (!SMS.isPhone(phoneNumber)) {
        reject({ code: 400, message: "手机号码格式不正确" });
        return;
      }
      // 校验uuid 和 验证码
      if (!uuid || !code) {
        reject({ code: 400, message: "验证码不能为空" });
        return;
      }
      try {
        // 获取redis中的验证码
        let data = await Redis.get(`sms-code-${phoneNumber}`);
        if (!data) {
          reject({ code: 400, message: "验证码已过期" });
          return;
        }
        data = JSON.parse(data);
        const bool = uuid === data.uuid && code === data.code;
        if (bool) {
          // 校验成功，删除redis中的验证码
          await Redis.del(`sms-code-${phoneNumber}`);
        }
        resolve(bool);
      } catch (error) {
        reject(error);
      }
    });
  }

  /**
   * 校验手机号码是否合法
   */
  static isPhone(phoneNumber) {
    return /^1[3-9]\d{9}$/.test(phoneNumber);
  }
}

module.exports = SMS;
